/*
 * Copyright 2022 MindMotion Microelectronics Co., Ltd.
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */


#include "board_init.h"
#include "hal_gpio.h"
#include "hal_qspi.h"

/*
* Definitions.
*/

/*
* Declerations.
*/
void BOARD_InitDebugConsole(void);
void BOARD_InitExtFlash(void);

/*
* Functions.
*/
void BOARD_Init(void)
{
    BOARD_InitBootClocks();
    BOARD_InitPins();

    BOARD_InitExtFlash();
}

static uint32_t ExtFlash_ReadJEDEC(void)
{
    QSPI_IndirectXferConf_Type xfer;
    xfer.CmdBusWidth   = QSPI_BusWidth_1b;
    xfer.CmdValue      = 0x9F; /* read jedec. */
    xfer.AddrBusWidth  = QSPI_BusWidth_None;
    xfer.AltBusWidth   = QSPI_BusWidth_None;
    xfer.DummyCycles   = 0;
    xfer.DataBusWidth  = QSPI_BusWidth_1b;
    xfer.DataWordWidth = QSPI_WordWidth_8b;
    xfer.DataLen       = 3;

    QSPI_ClearStatus(QSPI, QSPI_STATUS_XFER_DONE);
    QSPI_SetIndirectReadConf(QSPI, &xfer);
    uint32_t jedec = 0;
    while( (QSPI_STATUS_XFER_DONE | QSPI_STATUS_FIFO_EMPTY) != (QSPI_GetStatus(QSPI) & (QSPI_STATUS_XFER_DONE | QSPI_STATUS_FIFO_EMPTY) ) )
    {
        if ( 0u == (QSPI_GetStatus(QSPI) & QSPI_STATUS_FIFO_EMPTY) )
        {
            jedec <<= 8;
            jedec |= QSPI_GetIndirectData(QSPI);
        }
    }
    return jedec;
}

static bool ExtFlash_IsBusy(void)
{
    QSPI_IndirectXferConf_Type xfer;
    xfer.CmdBusWidth   = QSPI_BusWidth_1b;
    xfer.CmdValue      = 0x05; /* read reg-1. */
    xfer.AddrBusWidth  = QSPI_BusWidth_None;
    xfer.AltBusWidth   = QSPI_BusWidth_None;
    xfer.DummyCycles   = 0;
    xfer.DataBusWidth  = QSPI_BusWidth_1b;
    xfer.DataWordWidth = QSPI_WordWidth_8b;
    xfer.DataLen       = 1;

    QSPI_ClearStatus(QSPI, QSPI_STATUS_XFER_DONE);
    QSPI_SetIndirectReadConf(QSPI, &xfer);
    uint32_t reg_1 = 0;
    while( (QSPI_STATUS_XFER_DONE | QSPI_STATUS_FIFO_EMPTY) != (QSPI_GetStatus(QSPI) & (QSPI_STATUS_XFER_DONE | QSPI_STATUS_FIFO_EMPTY) ) )
    {
        if ( 0u == (QSPI_GetStatus(QSPI) & QSPI_STATUS_FIFO_EMPTY) )
        {
            reg_1 = QSPI_GetIndirectData(QSPI);
        }
    }
    return 0 != (reg_1 & 0x01); /* busy bit is set. */
}

static void ExtFlash_EnableWrite(bool enable)
{
    QSPI_IndirectXferConf_Type xfer;
    xfer.CmdBusWidth   = QSPI_BusWidth_1b;
    xfer.AddrBusWidth  = QSPI_BusWidth_None;
    xfer.AltBusWidth   = QSPI_BusWidth_None;
    xfer.DummyCycles   = 0;
    xfer.DataBusWidth  = QSPI_BusWidth_None;

    if (enable)
    {
        xfer.CmdValue  = 0x06; /* write enable. */
    }
    else
    {
        xfer.CmdValue  = 0x04; /* write disable. */
    }

    QSPI_ClearStatus(QSPI, QSPI_STATUS_XFER_DONE);
    QSPI_SetIndirectWriteConf(QSPI, &xfer);
    while(0 == (QSPI_GetStatus(QSPI) & QSPI_STATUS_XFER_DONE))
    {}
}

/* sck delay. */
static void softspi_delay(void)
{
    for (uint32_t i = BOARD_SOFTSPI_DELAY_CNT; i > 0; i--)
    {
        __NOP();
    }
}

/* xfer a byte. */
static uint8_t softspi_xfer_one_data(uint8_t tx_data)
{
    uint8_t rx_data = 0;
    GPIO_WriteBit(BOARD_SOFTSPI_SCK_GPIO_PORT, BOARD_SOFTSPI_SCK_GPIO_PIN, 1); /* pull up sck. */
    for (uint32_t i = 0; i < 8; i++)
    {
        softspi_delay(); /* sck keep. */
        GPIO_WriteBit(BOARD_SOFTSPI_SCK_GPIO_PORT , BOARD_SOFTSPI_SCK_GPIO_PIN , 0); /* pull down sck. */
        GPIO_WriteBit(BOARD_SOFTSPI_MOSI_GPIO_PORT, BOARD_SOFTSPI_MOSI_GPIO_PIN, (tx_data & 0x80) >> 7); /* write tx data. */
        tx_data <<= 1; /* MSB. */
        softspi_delay(); /* sck keep. */
        rx_data <<= 1; /* MSB. */
        rx_data |= GPIO_ReadInDataBit(BOARD_SOFTSPI_MISO_GPIO_PORT, BOARD_SOFTSPI_MISO_GPIO_PIN); /* read rx data. */
        GPIO_WriteBit(BOARD_SOFTSPI_SCK_GPIO_PORT , BOARD_SOFTSPI_SCK_GPIO_PIN , 1); /* pull up sck. */
    }
    return rx_data;
}

/* xfer data. */
void softspi_xfer(uint8_t * write_buf, uint32_t write_size, uint8_t * read_buf, uint32_t read_size)
{
    /* enable spi slave device. */
    GPIO_WriteBit(BOARD_SOFTSPI_NSS_GPIO_PORT, BOARD_SOFTSPI_NSS_GPIO_PIN, 0);

    /* write. */
    for(uint32_t i = 0; i < write_size; i++)
    {
        softspi_xfer_one_data(write_buf[i]);
    }

    /* read. */
    for (uint32_t i = 0; i < read_size; i++)
    {
        read_buf[i] = softspi_xfer_one_data(0xFF);
    }

    /* disable spi slave device. */
    GPIO_WriteBit(BOARD_SOFTSPI_NSS_GPIO_PORT, BOARD_SOFTSPI_NSS_GPIO_PIN, 1);
}

/* enable or disable 4line mode. */
void app_enable_4line_mode(bool enable)
{
    uint8_t enable_write_cmd_buf[]    = {0x06};
    uint8_t disable_write_cmd_buf[]   = {0x04};
    uint8_t read_register1_cmd_buf[]  = {0x05};
    uint8_t read_register2_cmd_buf[]  = {0x35};
    uint8_t write_register2_cmd_buf[] = {0x31, 0};
    uint8_t status_buf[] = {0};

    /* send "get register-2 status" cmd. */
    softspi_xfer(read_register2_cmd_buf, sizeof(read_register1_cmd_buf), status_buf, sizeof(status_buf)); /* read regsiter-2 status. */

    /* set or reset qe bit. */
    if (enable)
    {
        status_buf[0] |= 0x02;
        write_register2_cmd_buf[1] = status_buf[0];
    }
    else
    {
        status_buf[0] &= ~0x01;
        write_register2_cmd_buf[1] = status_buf[0];
    }

    softspi_xfer(enable_write_cmd_buf, sizeof(enable_write_cmd_buf), NULL, 0); /* enable write. */

    softspi_xfer(write_register2_cmd_buf, sizeof(write_register2_cmd_buf), NULL, 0); /* write register-2. */

    for (uint32_t i = 0; i < BOARD_SPIFLASH_WRITE_TIMEOUT; i++) /* wait for set qe bit done. */
    {
        softspi_xfer(read_register1_cmd_buf, sizeof(read_register1_cmd_buf), status_buf, sizeof(status_buf)); /* read regsiter-1 status. */
        if (0 == (status_buf[0] & 0x01)) /* check busy. */
        {
            break;
        }
    }

    softspi_xfer(disable_write_cmd_buf, sizeof(disable_write_cmd_buf), NULL, 0); /* disable write. */
}

void BOARD_InitExtFlash(void)
{
    GPIO_Init_Type gpio_init;

    /* PF6 - QSPI_CS. */
    gpio_init.Pins  = GPIO_PIN_6 | GPIO_PIN_10;
    gpio_init.PinMode  = GPIO_PinMode_Out_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOF, &gpio_init);
    GPIO_PinAFConf(GPIOF, gpio_init.Pins, GPIO_AF_15);
    GPIO_SetBits(GPIOF, gpio_init.Pins);

    gpio_init.Pins  = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8;
    gpio_init.PinMode  = GPIO_PinMode_Out_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOG, &gpio_init);
    GPIO_PinAFConf(GPIOG, gpio_init.Pins, GPIO_AF_15);
    GPIO_SetBits(GPIOG, gpio_init.Pins);

    /* PF8 - QSPI_IO1. */
    gpio_init.Pins  = GPIO_PIN_8;
    gpio_init.PinMode  = GPIO_PinMode_In_PullUp;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOF, &gpio_init);
    GPIO_PinAFConf(GPIOF, gpio_init.Pins, GPIO_AF_15);

    app_enable_4line_mode(true);

    /* init qspi hardware. */
    QSPI_Init_Type qspi_init;
    qspi_init.SckDiv            = BOARD_QSPI_SCK_DIV;
    qspi_init.CsHighLevelCycles = BOARD_QSPI_CS_HIGH_LEVEL_TIME;
    qspi_init.RxSampleDelay     = BOARD_QSPI_RX_DELAY_CYCLES;
    qspi_init.SpiMode           = QSPI_SpiMode_3; /* suggest using SPI Mode 3. */
    QSPI_Init(QSPI, &qspi_init);

    /* init qspi gpio. */
    gpio_init.Pins  = GPIO_PIN_6 | GPIO_PIN_8 | GPIO_PIN_10;
    gpio_init.PinMode  = GPIO_PinMode_AF_PushPull;
    gpio_init.Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOF, &gpio_init);
    GPIO_PinAFConf(GPIOF, gpio_init.Pins, GPIO_AF_10);

    gpio_init.Pins  = GPIO_PIN_6 | GPIO_PIN_7 | GPIO_PIN_8;
    GPIO_Init(GPIOG, &gpio_init);
    GPIO_PinAFConf(GPIOG, gpio_init.Pins, GPIO_AF_10);

    /* enable qspi direct read mode. */
    QSPI_DirectXferConf_Type direct_conf;
    direct_conf.CmdBusWidth   = BOARD_EXT_FLASH_CMD_BUS_WIDTH;
    direct_conf.CmdValue      = BOARD_EXT_FLASH_CMD_VALUE;
    direct_conf.AddrBusWidth  = BOARD_EXT_FLASH_ADDR_BUS_WIDTH;
    direct_conf.AddrWordWidth = BOARD_EXT_FLASH_ADDR_WORD_WIDTH;
    direct_conf.AltBusWidth   = QSPI_BusWidth_None;
    direct_conf.DummyCycles   = BOARD_EXT_FLASH_DUMMY_CYCLES;
    direct_conf.DataBusWidth  = BOARD_EXT_FLASH_DATA_BUS_WIDTH;

    QSPI_EnableDirectRead(QSPI, &direct_conf);
}

void BOARD_EraseSector4KB(uint32_t base)
{
    ExtFlash_EnableWrite(true);

    QSPI_IndirectXferConf_Type xfer;
    xfer.CmdBusWidth   = QSPI_BusWidth_1b;
    xfer.CmdValue      = 0x20; /* sector erase. */
    xfer.AddrBusWidth  = QSPI_BusWidth_1b;
    xfer.AddrWordWidth = QSPI_WordWidth_24b;
    xfer.AddrValue     = base & 0x0FFFF000;
    xfer.AltBusWidth   = QSPI_BusWidth_None;
    xfer.DummyCycles   = 0;
    xfer.DataBusWidth  = QSPI_BusWidth_None;

    QSPI_ClearStatus(QSPI, QSPI_STATUS_XFER_DONE);
    QSPI_SetIndirectWriteConf(QSPI, &xfer);
    while(0 == (QSPI_GetStatus(QSPI) & QSPI_STATUS_XFER_DONE))
    {}

    while(ExtFlash_IsBusy())
    {}

    ExtFlash_EnableWrite(false);
}

void BOARD_WriteSector4KB(uint32_t base, uint8_t * buf)
{
    base &= 0x0FFFF000;
    uint8_t * xfer_buf = buf;
    for (uint32_t i = 0; i < 16; i++, xfer_buf += 256)
    {
        ExtFlash_EnableWrite(true);

        QSPI_IndirectXferConf_Type xfer;
        xfer.CmdBusWidth   = QSPI_BusWidth_1b;
        xfer.CmdValue      = 0x02;
        xfer.AddrBusWidth  = QSPI_BusWidth_1b;
        xfer.AddrWordWidth = QSPI_WordWidth_24b;
        xfer.AddrValue     = base + (i << 8);
        xfer.AltBusWidth   = QSPI_BusWidth_None;
        xfer.DummyCycles   = 0u;
        xfer.DataBusWidth  = QSPI_BusWidth_1b;
        xfer.DataWordWidth = QSPI_WordWidth_8b;
        xfer.DataLen       = 256;

        /* clear xfer done status. */
        QSPI_ClearStatus(QSPI, QSPI_STATUS_XFER_DONE);
        QSPI_SetIndirectWriteConf(QSPI, &xfer);
        uint32_t send_cnt = 0;
        while( QSPI_STATUS_XFER_DONE != (QSPI_GetStatus(QSPI) & QSPI_STATUS_XFER_DONE) )
        {
            if ( 0u == (QSPI_GetStatus(QSPI) & QSPI_STATUS_FIFO_FULL) && send_cnt < 256)
            {
                QSPI_PutIndirectData(QSPI, xfer_buf[send_cnt]); /* put data. */
                send_cnt++;
            }
        }

        while(ExtFlash_IsBusy())
        {}
    }

    ExtFlash_EnableWrite(false);
}

/* EOF. */
